/*************************************************************************
Copyright (C) 2011-2013 cid

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*************************************************************************/

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// Manager class of UTVP, Singleton, Manage global params, initialize camera, handle input event, create and manage Layers/MessageLayers/SoundBuffers/etc.
/// Equivalent to the System Class in KiriKiri2
/// <para>Author: cid</para>
/// <para>Support: http://ukag.codeplex.com</para>
/// </summary>
[RequireComponent(typeof(UTVPStorages))]
public class UTVPSystem : MonoBehaviour
{
	/// <summary>
	/// On mouse move delegate.
	/// </summary>
	/// <param name='x'>
	/// Mouse position.
	/// </param>
	/// <param name='y'>
	/// Mouse position.
	/// </param>
	/// <param name='dx'>
	/// Distance of mouse movement since last frame
	/// </param>
	/// <param name='dy'>
	/// Distance of mouse movement since last frame
	/// </param>
	public delegate void OnMouseMoveDelegate(int x, int y, float dx, float dy);
	/// <summary>
	/// On mouse down delegate.
	/// </summary>
	/// <param name='x'>
	/// Mouse position.
	/// </param>
	/// <param name='y'>
	/// Mouse position.
	/// </param>
	/// <param name='key'>
	/// The mouse button KeyCode.
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	/// <returns>
	/// If the hook function returns true, the mouse down events are not continue processing by layers
	/// </returns>
	public delegate bool OnMouseDownDelegate(int x, int y, KeyCode key, int shiftState);
	/// <summary>
	/// On mouse up delegate.
	/// </summary>
	/// <param name='x'>
	/// Mouse position.
	/// </param>
	/// <param name='y'>
	/// Mouse position.
	/// </param>
	/// <param name='key'>
	/// The mouse button KeyCode.
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	/// <returns>
	/// If the hook function returns true, the mouse up events are not continue processing by layers
	/// </returns>
	public delegate bool OnMouseUpDelegate(int x, int y, KeyCode key, int shiftState);
	/// <summary>
	/// On key down delegate.
	/// </summary>
	/// <param name='key'>
	/// The key.
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	/// <returns>
	/// If the hook function returns true, the key down events are not continue processing by layers
	/// </returns>
	public delegate bool OnKeyDownDelegate(KeyCode key, int shiftState);
	/// <summary>
	/// On key up delegate.
	/// </summary>
	/// <param name='key'>
	/// The key.
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	/// <returns>
	/// If the hook function returns true, the key up events are not continue processing by layers
	/// </returns>
	public delegate bool OnKeyUpDelegate(KeyCode key, int shiftState);
	
	/// <summary>
	/// Aspect ratio mode.
	/// </summary>
	public enum ASPECT_RATIO_MODE
	{
		/// <summary>
		/// force rated ratio and leaving black(transparent) border.
		/// </summary>
		ForceRatedRatio,
		/// <summary>
		/// Hight based and aligned to the top left.
		/// </summary>
		Free
	}

	/// <summary>
	/// SaveData(core) of the class UTVPSystem.
	/// </summary>
	[Serializable]
	public class SaveData_Core
	{			
		/// <summary>
		/// Screen width.
		/// </summary>
		public int width = 800;
		/// <summary>
		/// Screen height.
		/// </summary>
		public int height = 600;
		/// <summary>
		/// Is full-screen.
		/// </summary>
		public bool fullScreen = false;
	}
	
	/// <summary>
	/// The <see cref="UTVPStorages"/> instance.
	/// </summary>
	protected UTVPStorages m_Storages = null;
	/// <summary>
	/// The <see cref="UTVPStorages"/> instance.
	/// </summary>
	public UTVPStorages Storages { get { return m_Storages; } }
	
	/// <summary>
	/// Max value of <see cref="UTVPLayer.absoluteIndex"/>, Constant.
	/// </summary>
	public const int AbsoluteMax = 40000;
	
	/// <summary>
	/// The aspect ratio mode.
	/// </summary>
	public ASPECT_RATIO_MODE AspectRatioMode = ASPECT_RATIO_MODE.ForceRatedRatio;
	/// <summary>
	/// The standard width of the game screen.
	/// </summary>
	protected int m_RatedWidth = 800;
	/// <summary>
	/// The standard height of the game screen.
	/// </summary>
	protected int m_RatedHeight = 600;
	/// <summary>
	/// Scaling relative to the standard size.
	/// </summary>
	protected float m_ScreenScale = 1.0f;
	/// <summary>
	/// The relative width of the game screen.
	/// </summary>
	protected int m_Width = 800;
	/// <summary>
	/// The relative height of the game screen.
	/// </summary>
	protected int m_Height = 600;
	/// <summary>
	/// The standard width of the game screen.
	/// </summary>
	public int Width { get { return m_Width; } }
	/// <summary>
	/// The standard height of the game screen.
	/// </summary>
	public int Height { get { return m_Height; } }
	/// <summary>
	/// The screen scale.
	/// </summary>
	public float ScreenScale { get { return 1.0f / m_ScreenScale; } }
	
	/// <summary>
	/// Camera for display layers and messagelayers.
	/// </summary>
	protected Camera m_UTVPCamera;
	/// <summary>
	/// Camera for display layers and messagelayers.
	/// </summary>
	public Camera UTVPCamera { get { return m_UTVPCamera; } }
	/// <summary>
	/// Gets the mouse position relative to the standard size.
	/// </summary>
	/// <value>
	/// The mouse position X.
	/// </value>
	public int CursorX { get { return (int)((Input.mousePosition.x - m_ViewPortPixelRect.x) * m_ScreenScale); } }
	/// <summary>
	/// Gets the mouse position relative to the standard size.
	/// </summary>
	/// <value>
	/// The mouse position Y.
	/// </value>
	public int CursorY { get { return m_Height - (int)((Input.mousePosition.y - m_ViewPortPixelRect.y) * m_ScreenScale); } }

	private float m_ZScale = 1.0f;
	private float m_CursorXLast;
	private float m_CursorYLast;
	private Rect m_ViewPortPixelRect = new Rect();
	private void RefreshViewPortRect()
	{
		if (AspectRatioMode == ASPECT_RATIO_MODE.ForceRatedRatio)
		{
			int viewPortWidth = m_RatedWidth * Screen.height / m_RatedHeight;
			int viewPortHeight = Screen.height;
			if (m_RatedWidth * Screen.height > Screen.width * m_RatedHeight)
			{
				viewPortWidth = Screen.width;
				viewPortHeight = m_RatedHeight * Screen.width / m_RatedWidth;
			}
			m_ViewPortPixelRect.x = (Screen.width - viewPortWidth) / 2;
			m_ViewPortPixelRect.y = (Screen.height - viewPortHeight) / 2;
			m_ViewPortPixelRect.width = viewPortWidth;
			m_ViewPortPixelRect.height = viewPortHeight;
		}
		else // ASPECT_RATIO_MODE.Free
		{
			m_ViewPortPixelRect.x = 0;
			m_ViewPortPixelRect.y = 0;
			m_ViewPortPixelRect.width = Screen.width;
			m_ViewPortPixelRect.height = Screen.height;
		}
	}
	
	/// <summary>
	/// Sets the size of the window.
	/// </summary>
	/// <param name='width'>
	/// Width of the window.
	/// </param>
	/// <param name='height'>
	/// Height of the window.
	/// </param>
	/// <param name='isFullScreen'>
	/// Is full screen.
	/// </param>
	public void SetWindowSize(int width, int height, bool isFullScreen)
	{
		width = Screen.width;
		height = Screen.height;
		ResetCamera(width, height);
	}
	
	/// <summary>
	/// Resets parameters of the camera.
	/// </summary>
	/// <param name='screenWidth'>
	/// Screen width.
	/// </param>
	/// <param name='screenHeight'>
	/// Screen height.
	/// </param>
	public void ResetCamera(float screenWidth, float screenHeight)
	{
		RefreshViewPortRect();
		Rect viewPortRect = m_ViewPortPixelRect;
		viewPortRect.x /= screenWidth;
		viewPortRect.y /= screenHeight;
		viewPortRect.width /= screenWidth;
		viewPortRect.height /= screenHeight;

		bool needsHalfPixelOffset = SystemInfo.graphicsDeviceVersion.IndexOf("Direct3D") >= 0 || Application.platform == RuntimePlatform.WindowsEditor;
		if (needsHalfPixelOffset)
			needsHalfPixelOffset = (SystemInfo.graphicsShaderLevel < 40);
		
		m_UTVPCamera.clearFlags = CameraClearFlags.Depth;
		m_UTVPCamera.cullingMask = 1 << UTVPConfig.LayerMask;
		m_UTVPCamera.depth = UTVPConfig.CameraDepth;
		m_UTVPCamera.orthographic = true;
		m_UTVPCamera.orthographicSize = 1;
		m_UTVPCamera.nearClipPlane = -5.0f;
		m_UTVPCamera.farClipPlane = 1.0f;
		if (AspectRatioMode == ASPECT_RATIO_MODE.ForceRatedRatio)
		{
			Vector3 pos = new Vector3(m_RatedWidth / 2, -m_RatedHeight / 2, 0);
			if (needsHalfPixelOffset)
			{
				pos.x += 0.5f;
				pos.y += 0.5f;
			}
			m_UTVPCamera.rect = viewPortRect;
			m_UTVPCamera.transform.localPosition = pos;
			m_Width = m_RatedWidth;
			m_Height = m_RatedHeight;
		}
		else
		{
			if (Screen.width / (float)Screen.height - m_RatedWidth / (float)m_RatedHeight < -0.01f) // width based
			{
				m_Width = m_RatedWidth;
				m_Height = m_RatedWidth * Screen.height / Screen.width;
			}
			else // height based
			{
				m_Width = m_RatedHeight * Screen.width / Screen.height;
				m_Height = m_RatedHeight;
			}
			Vector3 pos = new Vector3(m_Width / 2, -m_Height / 2, 0);
			if (needsHalfPixelOffset)
			{
				pos.x += 0.5f;
				pos.y += 0.5f;
			}
			m_UTVPCamera.rect = new Rect(0, 0, 1, 1);
			m_UTVPCamera.transform.localPosition = pos;
		}
		m_ZScale = m_Height / 2 / (float)AbsoluteMax * m_UTVPCamera.nearClipPlane;
		m_ScreenScale = m_Height / m_ViewPortPixelRect.height;
		this.transform.localScale = new Vector3(1.0f / m_Height * 2, 1.0f / m_Height * 2, 1.0f / m_Height * 2);
	}
	
	/// <summary>
	/// The root layer.
	/// </summary>
	protected UTVPLayer m_RootLayer = null;
	/// <summary>
	/// Gets the root layer.
	/// </summary>
	/// <value>
	/// The root layer.
	/// </value>
	public UTVPLayer RootLayer { get { return m_RootLayer; } }
	/// <summary>
	/// Finds the layer.
	/// </summary>
	/// <returns>
	/// The layer.
	/// </returns>
	/// <param name='name'>
	/// Layer Name.
	/// </param>
	public UTVPLayer FindLayer(string name)
	{
		if (name == null || name == "")
			return null;
		return m_RootLayer.FindLayerInChildren(name);
	}

	private System.Text.StringBuilder m_DrawingTextBuffer = new System.Text.StringBuilder(1024);
	/// <summary>
	/// Gets all the text drawing with the specified font
	/// </summary>
	/// <returns>
	/// text.
	/// </returns>
	/// <param name='font'>
	/// Font.
	/// </param>
	public string GetAllDrawingText(UTVPFont font)
	{
		m_DrawingTextBuffer.Remove(0, m_DrawingTextBuffer.Length);
		for (int i=0; i<m_CurrMsgLayers.Count; i++)
			if (m_CurrMsgLayers[i].Font.Name == font.Name)
				m_DrawingTextBuffer.Append(m_CurrMsgLayers[i].CurrContent);
		return m_DrawingTextBuffer.ToString();
	}
	
	/// <summary>
	/// Refresh the message layer text when the dynamic font texture is rebuild.
	/// </summary>
	/// <param name='font'>
	/// Font.
	/// </param>
	public void RefreshMessageLayerText(UTVPFont font)
	{
		for (int i=0; i<m_CurrMsgLayers.Count; i++)
			if (m_CurrMsgLayers[i].Font.Name == font.Name)
				m_CurrMsgLayers[i].RefreshTextMesh();
	}
	
	/// <summary>
	/// Ignore keyboard, mouse and touch input.
	/// </summary>
	public bool IgnoreInput = false;
	
	/// <summary>
	/// Gets the ray at the mouse position.
	/// </summary>
	/// <returns>
	/// The ray.
	/// </returns>
	public Ray GetMouseRay()
	{
		return m_UTVPCamera.ScreenPointToRay(Input.mousePosition);
	}
	
	/// <summary>
	/// The layer in the mouse position.
	/// </summary>
	private UTVPLayer m_MouseTargetLayer = null;
	
	/// <summary>
	/// Gets the layer in the mouse position.
	/// </summary>
	/// <value>
	/// The layer in the mouse position.
	/// </value>
	public UTVPLayer MouseTargetLayer { get { return m_MouseTargetLayer; } }
	
	public KeyCode[] listenedKeys;
	
	/// <summary>
	/// Gets the absoluteIndex of the layer.
	/// Camera.nearClipPlane is negative, layer's position is from nearClipPlane to 0.
	/// </summary>
	/// <returns>
	/// The layer absolute index.
	/// </returns>
	/// <param name='layerTransform'>
	/// Layer transform.
	/// </param>
	public int GetLayerAbsoluteIndex(Transform layerTransform)
	{
		return (int)(layerTransform.localPosition.z / m_ZScale + 0.1f);
	}
	
	/// <summary>
	/// Sets the absoluteIndex of the layer.
	/// Camera.nearClipPlane is negative, layer's position is from nearClipPlane to 0.
	/// </summary>
	/// <param name='absoluteIndex'>
	/// Absolute index.
	/// </param>
	/// <param name='layerTransform'>
	/// Layer transform.
	/// </param>
	public void SetLayerAbsoluteIndex(int absoluteIndex, Transform layerTransform)
	{
		Vector3 pos = layerTransform.localPosition;
		pos.z = absoluteIndex * m_ZScale;
		layerTransform.localPosition = pos;
		if (layerTransform.position.z <= m_UTVPCamera.nearClipPlane)
			Debug.LogWarning( "Layer " + layerTransform.name + " absoluteIndex value is overflow" );
		if (layerTransform.position.z >= m_UTVPCamera.farClipPlane)
			Debug.LogWarning( "Layer " + layerTransform.name + " absoluteIndex value is underflow" );
	}
	
	/// <summary>
	/// The mouse move hook.
	/// </summary>
	public OnMouseMoveDelegate MouseMoveHook = null;
	/// <summary>
	/// The mouse down hook.
	/// If the hook function returns true, the mouse down events are not continue processing by layers
	/// </summary>
	public OnMouseDownDelegate MouseDownHook = null;
	/// <summary>
	/// The mouse up hook.
	/// If the hook function returns true, the mouse up events are not continue processing by layers
	/// </summary>
	public OnMouseUpDelegate MouseUpHook = null;
	/// <summary>
	/// The key down hook.
	/// If the hook function returns true, the key down events are not continue processing by layers
	/// </summary>
	public OnKeyDownDelegate KeyDownHook = null;
	/// <summary>
	/// The key up hook.
	/// If the hook function returns true, the key up events are not continue processing by layers
	/// </summary>
	public OnKeyUpDelegate KeyUpHook = null;
	
	protected virtual void Awake () {
		this.gameObject.layer = UTVPConfig.LayerMask;
		m_Storages = GetComponent<UTVPStorages>();
		m_RatedWidth = UTVPConfig.RatedWidth;
		m_RatedHeight = UTVPConfig.RatedHeight;
		GameObject cameraObj = new GameObject("uiCamera");
		cameraObj.transform.parent = this.transform;
		m_UTVPCamera = cameraObj.AddComponent<Camera>();
		cameraObj.layer = this.gameObject.layer;
		SetWindowSize(m_RatedWidth, m_RatedHeight, false);
		m_RootLayer = UTVPLayer.CreateLayer<UTVPLayer>(this, null, "UTVPRootLayer", this.gameObject.layer);
		
		DontDestroyOnLoad(this.gameObject);
	}
	
	protected virtual void Start ()
	{
		m_CursorXLast = Input.mousePosition.x;
		m_CursorYLast = Input.mousePosition.y;
		
		m_RootLayer.Visible = true;
		m_RootLayer.Image = null;
		m_RootLayer.AbsoluteIndex = 0;
		m_RootLayer.SetPosAndSize(0, 0, Width, Height);
	}
	

	private List<UTVPMessageLayer> m_CurrMsgLayers = new List<UTVPMessageLayer>();
	protected internal List<UTVPMessageLayer> CurrMsgLayers { get { return m_CurrMsgLayers; } }
	private readonly KeyCode[] MOUSE_BUTTONS = {KeyCode.Mouse0, KeyCode.Mouse1, KeyCode.Mouse2}; // as a const 

	// Update is called once per frame
	protected virtual void Update ()
	{
		m_RootLayer.LayerUpdate();

		if (IgnoreInput)
			return;
		
		if (m_MouseTargetLayer != null && !m_MouseTargetLayer.NodeVisible)
			m_MouseTargetLayer = null;
		
		bool useTouchInput = false;
#if (UNITY_IPHONE || UNITY_ANDROID || UNITY_PSM) && !UNITY_EDITOR
		useTouchInput = true;
#endif

		int shiftState = 0;
		if (useTouchInput)
		{
			if (Input.touchCount > 0)
			{
				int x = (int)((Input.GetTouch(0).position.x - m_ViewPortPixelRect.x) * m_ScreenScale);
				int y = m_Height - (int)((Input.GetTouch(0).position.y - m_ViewPortPixelRect.y) * m_ScreenScale);
				float dx = Input.GetTouch(0).deltaPosition.x * m_ScreenScale;
				float dy = -Input.GetTouch(0).deltaPosition.y * m_ScreenScale;
				
				if (Input.GetTouch(0).phase == TouchPhase.Began || Input.GetTouch(0).phase == TouchPhase.Moved || Input.GetTouch(0).phase == TouchPhase.Stationary)
					shiftState += UTVPLayer.ssLeft;
				
				ProcessMouseMove(x, y, dx, dy, shiftState);
				
				if (Input.GetTouch(0).phase == TouchPhase.Began)
				{
					ProcessMouseDown(x, y, KeyCode.Mouse0, shiftState);
					if (m_MouseTargetLayer == null)
						OnAdvClick(KeyCode.Mouse0, shiftState);
				}
				else if (Input.GetTouch(0).phase == TouchPhase.Ended || Input.GetTouch(0).phase == TouchPhase.Canceled)
					ProcessMouseUp(x, y, KeyCode.Mouse0, shiftState);
			}
		}
		else
		{
			if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
				shiftState += UTVPLayer.ssShift;
			if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
				shiftState += UTVPLayer.ssCtrl;
			if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt))
				shiftState += UTVPLayer.ssAlt;
			if (Input.GetMouseButton(0))
				shiftState += UTVPLayer.ssLeft;
			if (Input.GetMouseButton(1))
				shiftState += UTVPLayer.ssRight;
			if (Input.GetMouseButton(2))
				shiftState += UTVPLayer.ssMiddle;
			
			int x = CursorX;
			int y = CursorY;
			float dx = (Input.mousePosition.x - m_CursorXLast) * m_ScreenScale;
			float dy = (m_CursorYLast - Input.mousePosition.y) * m_ScreenScale;
			if (dx != 0 || dy != 0)
			{
				ProcessMouseMove(x, y, dx, dy, shiftState);
				m_CursorXLast = Input.mousePosition.x;
				m_CursorYLast = Input.mousePosition.y;
			}
			
			for (int i=0; i<MOUSE_BUTTONS.Length; i++)
			{
				if (Input.GetKeyDown(MOUSE_BUTTONS[i]))
					ProcessMouseDown(x, y, MOUSE_BUTTONS[i], shiftState);
				else if (Input.GetKeyUp(MOUSE_BUTTONS[i]))
					ProcessMouseUp(x, y, MOUSE_BUTTONS[i], shiftState);
			}
		
			for (int i=0; i<listenedKeys.Length; i++)
			{
				if (listenedKeys[i] == KeyCode.Mouse0 || listenedKeys[i] == KeyCode.Mouse1 || listenedKeys[i] == KeyCode.Mouse2)
					continue;
				
				if (Input.GetKeyDown(listenedKeys[i]))
					ProcessKeyDown(listenedKeys[i], shiftState);
				else if (Input.GetKeyUp(listenedKeys[i]))
					ProcessKeyUp(listenedKeys[i], shiftState);
			}
				
			if (Input.GetKeyDown(KeyCode.Return))
			{
				OnEnterKey(KeyCode.Return, shiftState);
				if (m_MouseTargetLayer == null)
					OnAdvClick(KeyCode.Return, shiftState);
			}
			else if(Input.GetKeyDown(KeyCode.KeypadEnter))
			{
				OnEnterKey(KeyCode.KeypadEnter, shiftState);
				if (m_MouseTargetLayer == null)
					OnAdvClick(KeyCode.KeypadEnter, shiftState);
			}
			else if(Input.GetKey(KeyCode.LeftControl))
				OnAdvClick(KeyCode.LeftControl, shiftState);
			else if(Input.GetKey(KeyCode.RightControl))
				OnAdvClick(KeyCode.RightControl, shiftState);
			else if(m_MouseTargetLayer == null && Input.GetKeyDown(KeyCode.Space))
				OnAdvClick(KeyCode.Space, shiftState);
			else if(m_MouseTargetLayer == null && Input.GetKeyDown(KeyCode.Mouse0))
				OnAdvClick(KeyCode.Mouse0, shiftState);
		}
	}
	
	protected virtual void LateUpdate()
	{
		m_RootLayer.LayerLateUpdate();

		for (int i=0; i<m_CurrMsgLayers.Count; i++)
			m_CurrMsgLayers[i].MsgLayerLateUpdate();
	}
	
	/// <summary>
	/// Called when the Enter key is pressed.
	/// </summary>
	/// <param name='key'>
	/// key (KeyCode.Return or KeyCode.KeypadEnter).
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	protected virtual void OnEnterKey(KeyCode key, int shiftState) {}
	/// <summary>
	/// Called when the 'continue/skip' key is pressed.
	/// </summary>
	/// <param name='key'>
	/// key (KeyCode.Return, KeyCode.KeypadEnter, KeyCode.Space or KeyCode.Mouse0).
	/// </param>
	/// <param name='shiftState'>
	/// The state of the shift, ctrl, alt and mouse button.
	/// </param>
	protected virtual void OnAdvClick(KeyCode key, int shiftState) {}
	
	private void ProcessMouseMove(int x, int y, float dx, float dy, int shiftState)
	{
		if (MouseMoveHook != null)
			MouseMoveHook(x, y, dx, dy);
		
		if (m_MouseTargetLayer != null)
			m_MouseTargetLayer.OnMouseMove(x - m_MouseTargetLayer.LeftGlobal, y - m_MouseTargetLayer.TopGlobal, dx, dy, shiftState);
		
		m_MouseTargetLayer = m_RootLayer.ProcessMouseMove(x, y);
	}
	
	private void ProcessMouseDown(int x, int y, KeyCode key, int shiftState)
	{
		bool ignoreMouseEvent = false;
		if (MouseDownHook != null)
			ignoreMouseEvent = MouseDownHook(x, y, key, shiftState);
		if (ignoreMouseEvent)
			return;
		
		if (m_MouseTargetLayer != null)
			m_MouseTargetLayer.OnMouseDown(x - m_MouseTargetLayer.LeftGlobal, y - m_MouseTargetLayer.TopGlobal, key, shiftState);
	}
	private void ProcessMouseUp(int x, int y, KeyCode key, int shiftState)
	{
		bool ignoreMouseEvent = false;
		if (MouseUpHook != null)
			ignoreMouseEvent = MouseUpHook(x, y, key, shiftState);
		if (ignoreMouseEvent)
			return;
		
		if (m_MouseTargetLayer != null)
			m_MouseTargetLayer.OnMouseUp(x - m_MouseTargetLayer.LeftGlobal, y - m_MouseTargetLayer.TopGlobal, key, shiftState);
	}
	
	private bool ProcessKeyDown(KeyCode key, int shiftState)
	{
		if (KeyDownHook != null)
			return KeyDownHook(key, shiftState);
		return false;
	}
	
	private bool ProcessKeyUp(KeyCode key, int shiftState)
	{
		if (KeyUpHook != null)
			return KeyUpHook(key, shiftState);
		return false;
	}
	
	private Texture2D currScreenshotTexture = null;
	/// <summary>
	/// Gets the screenshot.
	/// </summary>
	/// <param name='scale'>
	/// Scale.
	/// </param>
	public void GetScreenshot(int scale)
	{
		StartCoroutine(GetScreenshot_impl(scale));
	}
	private IEnumerator GetScreenshot_impl(int scale)
	{
		Texture2D texOrigin = new Texture2D((int)m_ViewPortPixelRect.width, (int)m_ViewPortPixelRect.height);
		texOrigin.ReadPixels(m_ViewPortPixelRect, 0, 0, false);
		if (scale > 1)
		{
			yield return null;
			int mipmap = scale - 1;
			texOrigin.Apply();
			int dstW = ((int)m_ViewPortPixelRect.width) >> (mipmap);
			int dstH = ((int)m_ViewPortPixelRect.height) >> (mipmap);
			Texture2D texDst = new Texture2D(dstW, dstH);
			texDst.SetPixels32(texOrigin.GetPixels32(mipmap));
			currScreenshotTexture = texDst;
			Destroy(texOrigin);
		}
		else
			currScreenshotTexture = texOrigin;
		yield return null;
	}
	/// <summary>
	/// Saves the screenshot image to disk and return the Texture object.
	/// </summary>
	/// <returns>
	/// The screenshot Texture object.
	/// </returns>
	/// <param name='path'>
	/// The screenshot image save path.
	/// </param>
	public Texture2D SaveScreenshot(string path)
	{
		if (currScreenshotTexture != null)
		{
			currScreenshotTexture.Apply();
			FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
			byte[] pngData = currScreenshotTexture.EncodeToPNG();
			fs.Write(pngData, 0, pngData.Length);
			fs.Close();
			return currScreenshotTexture;
		}
		return null;
	}
	
	protected virtual SaveData_Core CreateSysSaveDataBuff()
	{
		return new SaveData_Core();
	}
	
	protected virtual void GetSelfSysData(SaveData_Core sysDataBuff)
	{
		sysDataBuff.width = Screen.width;
		sysDataBuff.height = Screen.height;
		sysDataBuff.fullScreen = Screen.fullScreen;
	}
	
	/// <summary>
	/// Gets the SaveData(core) to store.
	/// </summary>
	/// <returns>
	/// The save data(core).
	/// </returns>
	public SaveData_Core GetSysData()
	{
		SaveData_Core sysDataBuff = CreateSysSaveDataBuff();
		GetSelfSysData(sysDataBuff);
		
		return sysDataBuff;
	}
	
	protected virtual void SetSelfSysData(SaveData_Core sysDataBuff) {}
	
	/// <summary>
	/// Sets the core data from SaveData.
	/// </summary>
	/// <param name='sysDataBuff'>
	/// The save data(core).
	/// </param>
	public void SetSysData(SaveData_Core sysDataBuff)
	{
		SetSelfSysData(sysDataBuff);

		SetWindowSize(sysDataBuff.width, sysDataBuff.height, sysDataBuff.fullScreen);
	}
	
	/// <summary>
	/// Creates the layer.
	/// </summary>
	/// <returns>
	/// The layer.
	/// </returns>
	/// <param name='name'>
	/// Layer name.
	/// </param>
	/// <typeparam name='T'>
	/// Layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateLayer<T>(string name) where T : UTVPLayer
	{
		return CreateLayer<T>(name, m_RootLayer);
	}
	/// <summary>
	/// Creates the layer.
	/// </summary>
	/// <returns>
	/// The layer.
	/// </returns>
	/// <param name='name'>
	/// Layer name.
	/// </param>
	/// <param name='parent'>
	/// Parent layer.
	/// </param>
	/// <typeparam name='T'>
	/// Layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateLayer<T>(string name, UTVPLayer parent) where T : UTVPLayer
	{
		return CreateLayer<T>(name, parent, parent.gameObject.layer);
	}
	/// <summary>
	/// Creates the layer.
	/// </summary>
	/// <returns>
	/// The layer.
	/// </returns>
	/// <param name='name'>
	/// Layer name.
	/// </param>
	/// <param name='parent'>
	/// Parent layer.
	/// </param>
	/// <param name='gameObjLayer'>
	/// Game object layer.
	/// </param>
	/// <typeparam name='T'>
	/// Layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateLayer<T>(string name, UTVPLayer parent, int gameObjLayer) where T : UTVPLayer
	{
		return UTVPLayer.CreateLayer<T>(this, parent, name, gameObjLayer);
	}
	
	/// <summary>
	/// Creates the message layer.
	/// </summary>
	/// <returns>
	/// The message layer.
	/// </returns>
	/// <param name='name'>
	/// Message layer name.
	/// </param>
	/// <typeparam name='T'>
	/// Message layer type, <see cref="UTVPMessageLayer"/> or UTVPMessageLayer derived class.
	/// </typeparam>
	/// <typeparam name='FrameT'>
	/// Frame layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateMessageLayer<T, FrameT>(string name) where T : UTVPMessageLayer where FrameT : UTVPLayer
	{
		return CreateMessageLayer<T, FrameT>(name, m_RootLayer);
	}
	/// <summary>
	/// Creates the message layer.
	/// </summary>
	/// <returns>
	/// The message layer.
	/// </returns>
	/// <param name='name'>
	/// Message layer name.
	/// </param>
	/// <param name='parent'>
	/// Parent layer.
	/// </param>
	/// <typeparam name='T'>
	/// Message layer type, <see cref="UTVPMessageLayer"/> or UTVPMessageLayer derived class.
	/// </typeparam>
	/// <typeparam name='FrameT'>
	/// Frame layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateMessageLayer<T, FrameT>(string name, UTVPLayer parent) where T : UTVPMessageLayer where FrameT : UTVPLayer
	{
		return CreateMessageLayer<T, FrameT>(name, parent, parent.gameObject.layer, true, UTVPConfig.MessageLayerBuffLength);
	}
	/// <summary>
	/// Creates the message layer.
	/// </summary>
	/// <returns>
	/// The message layer.
	/// </returns>
	/// <param name='name'>
	/// Message layer name.
	/// </param>
	/// <param name='parent'>
	/// Parent layer.
	/// </param>
	/// <param name='createClickGlyph'>
	/// Whether to create click glyph.
	/// </param>
	/// <param name='charBufferLength'>
	/// Default char buffer length.
	/// </param>
	/// <typeparam name='T'>
	/// Message layer type, <see cref="UTVPMessageLayer"/> or UTVPMessageLayer derived class.
	/// </typeparam>
	/// <typeparam name='FrameT'>
	/// Frame layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateMessageLayer<T, FrameT>(string name, UTVPLayer parent, bool createClickGlyph, int charBufferLength) where T : UTVPMessageLayer where FrameT : UTVPLayer
	{
		return CreateMessageLayer<T, FrameT>(name, parent, parent.gameObject.layer, createClickGlyph, charBufferLength);
	}
	/// <summary>
	/// Creates the message layer.
	/// </summary>
	/// <returns>
	/// The message layer.
	/// </returns>
	/// <param name='name'>
	/// Message layer name.
	/// </param>
	/// <param name='parent'>
	/// Parent layer.
	/// </param>
	/// <param name='gameObjLayer'>
	/// Game object layer.
	/// </param>
	/// <param name='createClickGlyph'>
	/// Whether to create click glyph.
	/// </param>
	/// <param name='charBufferLength'>
	/// Default char buffer length.
	/// </param>
	/// <typeparam name='T'>
	/// Message layer type, <see cref="UTVPMessageLayer"/> or UTVPMessageLayer derived class.
	/// </typeparam>
	/// <typeparam name='FrameT'>
	/// Frame layer type, <see cref="UTVPLayer"/> or UTVPLayer derived class.
	/// </typeparam>
	public T CreateMessageLayer<T, FrameT>(string name, UTVPLayer parent, int gameObjLayer, bool createClickGlyph, int charBufferLength) where T : UTVPMessageLayer where FrameT : UTVPLayer
	{
		return UTVPMessageLayer.CreateMessageLayer<T, FrameT>(this, parent, name, gameObjLayer, createClickGlyph, charBufferLength);
	}

	/// <summary>
	/// Creates the sound buffer.
	/// </summary>
	/// <returns>
	/// The sound buffer.
	/// </returns>
	/// <param name='name'>
	/// Sound buffer name.
	/// </param>
	/// <typeparam name='T'>
	/// Sound buffer type, <see cref="UTVPSoundBuffer"/> or UTVPSoundBuffer derived class.
	/// </typeparam>
	public T CreateSoundBuffer<T>(string name) where T : UTVPSoundBuffer
	{
		return CreateSoundBuffer<T>(name, this.gameObject.layer);
	}
	/// <summary>
	/// Creates the sound buffer.
	/// </summary>
	/// <returns>
	/// The sound buffer.
	/// </returns>
	/// <param name='name'>
	/// Sound buffer name.
	/// </param>
	/// <param name='gameObjLayer'>
	/// Game object layer.
	/// </param>
	/// <typeparam name='T'>
	/// Sound buffer type, <see cref="UTVPSoundBuffer"/> or UTVPSoundBuffer derived class.
	/// </typeparam>
	public T CreateSoundBuffer<T>(string name, int gameObjLayer) where T : UTVPSoundBuffer
	{
		return UTVPSoundBuffer.CreateSoundBuffer<T>(this, name, gameObjLayer);
	}
}
